package com.jkgh.remedium;

import java.util.HashMap;
import java.util.Map;

import com.jkgh.asin.ConnectionListener;
import com.jkgh.asin.SelectorThread;
import com.jkgh.asin.State;
import com.jkgh.asin.StateMachine;
import com.jkgh.remedium.objects.RemediumObject;
import com.jkgh.remedium.objects.messages.out.RemediumS2CInitializer;
import com.jkgh.remedium.states.AuthenticateState;

public class RemediumServer extends RemediumHost implements ConnectionListener {
	
	private final SelectorThread selectorThread;
	
	private final Map<StateMachine, RemediumServerClientConnection> byMachines;
	private final Map<Integer, RemediumServerClientConnection> byIDs;
	
	private final RemediumHostListener listener;
	private final RemediumObject mainObject;
	
	private int hostIDGenerator;
	private int objectIDGenerator;

	public RemediumServer(RemediumObject object, RemediumHostListener listener) {
		this.mainObject = object;
		this.selectorThread = new SelectorThread(RemediumConstants.PORT, this);
		this.listener = listener;
		this.byMachines = new HashMap<StateMachine, RemediumServerClientConnection>();
		this.byIDs = new HashMap<Integer, RemediumServerClientConnection>();
		
		this.hostIDGenerator = 1;
		this.objectIDGenerator = 0;		
	}

	public void start() {
		selectorThread.start();
	}

	@Override
	public State connected(StateMachine machine) {
		RemediumServerClientConnection connection = new RemediumServerClientConnection(this, machine);
		return new AuthenticateState(connection);
	}

	@Override
	public void disconnected(StateMachine machine) {
		RemediumServerClientConnection connection = byMachines.remove(machine);
		if (connection != null) {
			int hostID = connection.getHostID();
			byIDs.remove(hostID);
			listener.disconnected(hostID);
		}
	}

	public void onAuthenticated(RemediumServerClientConnection connection) {
		StateMachine machine = connection.getMachine();
		byMachines.put(machine, connection);
		
		int hostID = nextHostID();
		byIDs.put(hostID, connection);
		connection.assignHostID(hostID);
		connection.send(new RemediumS2CInitializer(hostID, mainObject));
		
		listener.connected(hostID);
		
		connection.awaitOrders();
	}

	private int nextHostID() {
		return hostIDGenerator++;
	}

	public RemediumHost getHost(int hostID) {
		if (hostID == 0) {
			return this;
		} else {
			return byIDs.get(hostID);
		}
	}

	public int nextObjectID() {
		return objectIDGenerator++;
	}

	@Override
	public StateMachine getMachine() {
		throw new IllegalAccessError("RemediumServer does not posses a StateMachine by itself. Methods of server's objects can be called directly.");
	}
}
